#include "SpringAIInterface.h"

#include "../OOInterface/AIInterface.h"
#include "Commands/SpringCommand.h"
#include "Commands/SpringBuildCommand.h"
#include "Factories/SpringCommandFactory.h"
#include "../OOInterface/Interfaces/AIHandle.h"
#include "Factories/ReverseCommandFactory.h"
#include "UnitDirector.h"
#include "../OOInterface/Objects/Units/CUnitBuilder.h"
#include "../OOInterface/Objects/Team.h"

#include "../OOInterface/Objects/Events/EnemyEnterLOS.h"
#include "../OOInterface/Objects/Events/EnemyLeaveLOS.h"
#include "../OOInterface/Objects/Events/EnemyEnterRadar.h"
#include "../OOInterface/Objects/Events/EnemyLeaveRadar.h"
#include "../OOInterface/Objects/Events/UnitCreatedEvent.h"
#include "../OOInterface/Objects/Events/UnitFinishedEvent.h"
#include "../OOInterface/Objects/Events/UnitIdleEvent.h"
#include "../OOInterface/Objects/Events/UnitMoveFailedEvent.h"
#include "../OOInterface/Objects/Events/UnitDestroyedEvent.h"
#include "../OOInterface/Objects/Events/EnemyDestroyedEvent.h"
#include "../OOInterface/Objects/Events/UnitDamaged.h"
#include "../OOInterface/Objects/Events/ChatEvent.h"

#include "Sim/Features/Feature.h"
#include "Sim/Features/FeatureDef.h"



//Remove Test Code at a later date
#include "../Test/MessageInterpreter.h"
#include "../OOInterface/Objects/Logger.h"

SpringAIInterface::SpringAIInterface() :
	_callback(NULL), _units(), _gameMap(NULL),
	_metal( std::string("Metal"), std::string("Metal")),
	_energy( std::string("Energy"), std::string("Energy"))
{

	_idCache = new int[10000];
}

SpringAIInterface::~SpringAIInterface()
{
	//Destruct in the reverse order of the initialization

	_aiThread.interrupt();
	_aiThread.join();

	delete _ai;
	delete _gameMap;
	delete _idCache;
}

void SpringAIInterface::InitAI( IGlobalAICallback* callback, int team )
{
	_callback = callback;

	//OOInterface::Logger::getInstance("AI.txt") << "InitTeam: " << team << std::endl;

	std::stringstream ss;
	ss << "Number of CPU cores available: " << boost::thread::hardware_concurrency();
	sendTextMessage( ss.str() );

	initGameMap();
	createUnitPrototypes();

	_enemyTeam = OOInterface::TeamPtr( new OOInterface::Team() );
	_friendlyTeam = OOInterface::TeamPtr( new OOInterface::Team() );

	initPlayers();

	for( OOInterface::ConstPlayerSet::iterator it = _players.begin(); it != _players.end(); ++it )
	{
		OOInterface::ConstPlayerPtr ptr = *it;

		if( ptr->getID() == team )
		{
			if( _ai )
				_ai->initialize( ptr );

			_friendlyTeam->addPlayer( ptr );

			OOInterface::Player* player = const_cast< OOInterface::Player* >( ptr.get() );
			player->changeTeam( _friendlyTeam );

			_self = OOInterface::PlayerPtr( player );

			updatePlayers();
			break;
		}
	}

	_aiThread = boost::thread( boost::bind( &OOInterface::AIHandle::runThread, _ai) );
}

void SpringAIInterface::UnitCreated( int unit )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::UnitCreated> " << unit << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::UnitCreatedEvent( this->getUnitByID( unit ) ) ) );
}

void SpringAIInterface::UnitFinished( int unit )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::UnitFinished> " << unit << std::endl;
	OOInterface::UnitPtr unitPtr = this->getUnitByID( unit );
	unitPtr->setBuiltState( true );
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::UnitFinishedEvent( unitPtr ) ) );
}

void SpringAIInterface::UnitDestroyed( int unit, int attacker )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::UnitDestroyed> " << unit << " " << attacker << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::UnitDestroyedEvent( this->getUnitByID( unit ), this->getUnitByID( attacker ) ) ) );
}

void SpringAIInterface::EnemyEnterLOS( int enemy )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::EnemyEnterLOS> " << enemy << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::EnemyEnterLOS( this->getUnitByID( enemy ) ) ) );
}

void SpringAIInterface::EnemyLeaveLOS( int enemy )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::EnemyLeaveLOS> " << enemy << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::EnemyLeaveLOS( this->getUnitByID( enemy ) ) ) );
}

void SpringAIInterface::EnemyEnterRadar( int enemy )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::EnemyEnterRadar> " << enemy << std::endl;
	//Enemies entering radar doesn't give away their location or type in Spring so this will just alert that the radar has been triggered
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::EnemyEnterRadar() ) );
}

void SpringAIInterface::EnemyLeaveRadar( int enemy )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::EnemyLeaveRadar> " << enemy << std::endl;
	//Enemies entering radar doesn't give away their location or type in Spring so this will just alert that the radar has been triggered
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::EnemyLeaveRadar() ) );
}

void SpringAIInterface::EnemyDamaged( int damaged, int attacker, float damage, float3 dir )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::EnemyDamaged> " << damaged << " " << attacker << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::UnitDamaged( this->getUnitByID( damaged ), this->getUnitByID( attacker), damage) ) );
}

void SpringAIInterface::EnemyDestroyed( int enemy, int attacker )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::EnemyDestroyed> " << enemy << " " << attacker << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::EnemyDestroyedEvent( this->getUnitByID( enemy ), this->getUnitByID( attacker ) ) ) );
}

void SpringAIInterface::UnitIdle( int unit )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::UnitDestroyed> " << unit << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::UnitIdleEvent( this->getUnitByID( unit ) ) ) );
}

void SpringAIInterface::GotChatMsg( const char* msg, int playerID )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::GotChatMsg> " << msg << " " << playerID << std::endl;
	static MessageInterpreter interpreter;

	interpreter.interpret( std::string(msg), getAICallback(), this);

	for( OOInterface::ConstPlayerSet::iterator player = _players.begin(); player != _players.end(); ++player )
	{
		if( (*player)->getID() == playerID )
			this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::ChatEvent( *player, std::string(msg) )));
	}


}

void SpringAIInterface::UnitDamaged( int damaged, int attacker, float damage, float3 dir )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::UnitDamaged> " << damaged << " " << attacker << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::UnitDamaged( this->getUnitByID( damaged ), this->getUnitByID( attacker), damage) ) );
}

void SpringAIInterface::UnitMoveFailed( int unit )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::GotChatMsg> " << unit << std::endl;
	this->processGameEvent( OOInterface::GameEventPtr( new OOInterface::UnitMoveFailedEvent( this->getUnitByID( unit ) ) ) );
}

int SpringAIInterface::HandleEvent( int msg, const void* data )
{
	//TODO: Look into whether these HandleEvents are something or not
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::HandleEvent> " << msg << std::endl;
	return 1;
}

void SpringAIInterface::Update()
{
	//TODO: Update the GameMap as well as Unit's volatile state here periodically
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::Update> " << std::endl;
	flushBuffers();

	updateFriendlyUnits();
	updateEnemyUnits();

	updatePlayers();
}

void SpringAIInterface::sendTextMessage( const std::string& message, OOInterface::Player* player, bool team )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::sendTextMessage> " << message << std::endl;
	//As far as I'm aware, the second parameter of SendTextMessage has no affect


	//TODO: Remove this once done debugging
	//OOInterface::Logger& log = OOInterface::Logger::getInstance("AI.txt");
	//log << "-------------------------From sendTextMessage---------------------" << std::endl;
	//log << message << std::endl;
	//log << "------------------------------------------------------------------" << std::endl;


	//Thread-safe version, messages spammed on Spring's update() call
	insertTextBuffer( message );

	//Old Version
	//getAICallback()->SendTextMsg( message.c_str(), 0);
}

bool SpringAIInterface::orderUnit( OOInterface::ConstUnitPtr unit, OOInterface::ConstCommandPtr command)
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::orderUnit> " << std::endl;
	//This is the thread safe version
	//Commands are actually executed during the update call from Spring


	OOInterface::UnitPtr unitPtr = boost::const_pointer_cast< OOInterface::Unit > ( unit );

	if( unitPtr.get() && command.get() )
		insertCommandBuffer( unit, command );
	else
		OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::orderUnit> Received a NULL Unit or Command" << std::endl;

	/**
	const SpringCommand* cmd = dynamic_cast< const SpringCommand* > ( command );

	if( cmd )
		getAICallback()->GiveOrder( unit->getID(), cmd->getCommand());
	else
		OOInterface::Logger::getInstance("AI.txt") << "Error converting the command to a SpringCommand!!!" << std::endl;

	 **/

	//TODO: Do checking to see if the unit understood the command
	return true;
}

const OOInterface::ConstPlayerSet& SpringAIInterface::getPlayers()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::getPlayers> " << std::endl;
	return _players;
}

void SpringAIInterface::initPlayers()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::initPlayers> " << std::endl;
	std::list< OOInterface::Faction > factionList;

	//Spring supports only a max of 16 players
	for( int i = 0; i < 15; ++i ) {
		if( getAICallback()->GetTeamSide( i+1 ) == NULL)
			break; // GAIA is always the last player in the team listing so break out of the loop

		OOInterface::PlayerPtr player( new OOInterface::Player( i ) );

		OOInterface::Faction faction;

		//Find and set the Player's faction (if the Faction doesn't exist yet, create it)
		for( std::list< OOInterface::Faction >::iterator it = factionList.begin(); it != factionList.end(); ++it )
		{
			if( (*it).getName().compare( getAICallback()->GetTeamSide( i )) == 0 )
			{
				faction = *it;
				break;
			}
		}

		if( faction.getName().empty() )
		{
			faction = OOInterface::Faction( std::string(getAICallback()->GetTeamSide( i )) );
			factionList.push_back( faction );
		}

		player->setFaction( faction );

		_players.insert( player );
	}
}

void SpringAIInterface::updatePlayers()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::updatePlayers> " << std::endl;

	//Only necessary to update information about the AI that is using this information
	//Information about other players is considered "private" and thus inaccessible

	_self->setResourceAmount( _metal, getAICallback()->GetMetal() );
	_self->setResourceAmount( _energy, getAICallback()->GetEnergy() );

	_self->setResourceStorage( _metal, getAICallback()->GetMetalStorage() );
	_self->setResourceStorage( _energy, getAICallback()->GetEnergyStorage() );

	_self->setResourceUsage( _metal, getAICallback()->GetMetalUsage() );
	_self->setResourceUsage( _energy, getAICallback()->GetEnergyUsage() );

	_self->setResourceIncome( _metal, getAICallback()->GetMetalIncome() );
	_self->setResourceIncome( _energy, getAICallback()->GetEnergyIncome() );

	//_self is always on the friendlyTeam and _self's Units are updated in the updateUnit method
}

void SpringAIInterface::updateFriendlyUnits()
{
	int* unitIDs = _idCache;
	int totalFriendly = getAICallback()->GetFriendlyUnits( unitIDs );
	std::queue< OOInterface::UnitPtr > graveyard;


	for( int i =0; i < totalFriendly; ++i )
	{

		bool found = false;
		for( OOInterface::UnitSet::iterator unit = _units.begin(); unit != _units.end(); ++unit )
		{

			if( (*unit)->getID() == unitIDs[i] ) {
				found = true;

				if( !_friendlyTeam->has( (*unit )->getOwner() ) ) // Make sure teams are still accurate
				{
					OOInterface::Player* player = const_cast< OOInterface::Player* >( (*unit )->getOwner().get() );
					player->changeTeam( _friendlyTeam );
					_friendlyTeam->addPlayer( (*unit)->getOwner() );
					if( _enemyTeam->has( (*unit)->getOwner() )) //Could have switched teams
						_enemyTeam->removePlayer( (*unit)->getOwner() );

				}


				float3 pos = getAICallback()->GetUnitPos( unitIDs[i] );
				(*unit)->setX( pos.x/8 );
				(*unit)->setZ( pos.z/8 );
				(*unit)->setY( _gameMap->getElevation( (*unit)->getX(), (*unit)->getZ() ));

				if( (*unit)->getOwner()->getID() == _self->getID() )
				{
					const CCommandQueue* cmdQueue = getAICallback()->GetCurrentUnitCommands( unitIDs[i] );

					(*unit)->clearCommandQueue();

					ReverseCommandFactory reverseFactory;


					if( cmdQueue != NULL )
					{
						for( CCommandQueue::const_iterator it = cmdQueue->begin(); it != cmdQueue->end(); ++it )
						{
							OOInterface::ConstCommandPtr command = reverseFactory.createCommandObject( *it, _units, _unitPrototypes, getCommandFactory() );
							(*unit)->addCommand( command );
						}

						(*unit)->setIdle( cmdQueue->empty() );
					}
				}




				float health = getAICallback()->GetUnitHealth( unitIDs[i] );

				if( health == 0.0f )
				{
					//Unit death, Add it to the graveyard and remove from the Player's list of units
					graveyard.push( (*unit ) );
					OOInterface::ConstPlayerPtr owner = (*unit)->getOwner();

					OOInterface::Player* player = const_cast< OOInterface::Player* >( owner.get() );
					player->removeUnit( (*unit ) );
				}
				else
				{
					(*unit)->setHealth( getAICallback()->GetUnitHealth( unitIDs[i] ) );
				}

				break;

			}
		}


		while( !graveyard.empty() )
		{
			_units.erase( graveyard.front() );
			graveyard.pop();
		}


		//if(!found)
			//createUnit( unitIDs[i] );
	}

}

void SpringAIInterface::updateEnemyUnits()
{
	int* unitIDs = _idCache;
	std::queue< OOInterface::UnitPtr > graveyard;
	int totalEnemy = getAICallback()->GetEnemyUnits( unitIDs );


	for( int i =0; i < totalEnemy; ++i )
	{
		bool found = false;

		for( OOInterface::UnitSet::iterator unit = _units.begin(); unit != _units.end(); ++unit )
		{
			if( (*unit)->getID() == unitIDs[i] ) {
				found = true;

				if( !_enemyTeam->has( (*unit )->getOwner() ) ) // Make sure teams are still accurate
				{
					OOInterface::Player* player = const_cast< OOInterface::Player* >( (*unit )->getOwner().get() );
					player->changeTeam( _friendlyTeam );
					_enemyTeam->addPlayer( (*unit)->getOwner() );

					if( _friendlyTeam->has( (*unit)->getOwner() )) //Could have changed teams
						_friendlyTeam->removePlayer( (*unit)->getOwner() );
				}



				float3 pos = getAICallback()->GetUnitPos( unitIDs[i] );

				(*unit)->setX( pos.x/8 );
				(*unit)->setZ( pos.z/8 );
				(*unit)->setY( _gameMap->getElevation( (*unit)->getX(), (*unit)->getZ() ));

				float health = getAICallback()->GetUnitHealth( unitIDs[i] );

				if( health == 0.0f )
				{
					//Unit death, Add it to the graveyard and remove from the Player's list of units
					graveyard.push( (*unit ) );
					OOInterface::ConstPlayerPtr owner = (*unit)->getOwner();

					OOInterface::Player* player = const_cast< OOInterface::Player* >( owner.get() );
					player->removeUnit( (*unit ) );
				}
				else
				{
					(*unit)->setHealth( getAICallback()->GetUnitHealth( unitIDs[i] ) );
				}

				break;
			}
		}

		while( !graveyard.empty() )
		{
			_units.erase( graveyard.front() );
			graveyard.pop();
		}

		if(!found)
			createUnit( unitIDs[i] );
	}

}

void SpringAIInterface::initGameMap()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::initGameMap> " << std::endl;
	_gameMap = new OOInterface::GameMap(getAICallback()->GetMapWidth(), getAICallback()->GetMapHeight() );


	// Metal Map is 1/2 the resolution of the standard map
	// : cb->GetMapWidth()/2 * cb->GetMapHeight()/2;
	const unsigned char* metalMap = getAICallback()->GetMetalMap();
	const float* heightMap = getAICallback()->GetHeightMap();

	for( unsigned int x = 0; x < _gameMap->getWidth(); ++x)
		for( unsigned int y = 0; y < _gameMap->getHeight(); ++y)
		{
			_gameMap->setElevation( x, y, heightMap[ y* _gameMap->getWidth() + x] );
			if( metalMap[ y/2 * _gameMap->getWidth()/2 + x/2 ] != 0.0f )
				_gameMap->setResourceDensity(_metal, x, y, (float( metalMap[ y/2 * _gameMap->getWidth()/2 + x/2 ])/255 * getAICallback()->GetMaxMetal()));

		}


	//TODO: Check to see if cb->GetFeatures( int*, int ) is ever fixed
	int* featureIDs = new int[10000];
	int tFeatures = getAICallback()->GetFeatures( featureIDs, 10000, float3(0.0f,0.0f,0.0f), 1000000.0f);


	for( int i =0; i < tFeatures; ++i )
	{
		const FeatureDef* def = getAICallback()->GetFeatureDef( featureIDs[i] );
		float3 pos = getAICallback()->GetFeaturePos( featureIDs[i] );

		if( def->geoThermal )
		{
			//The number being assigned to the density of the geothermal node doesn't matter
			//As long as it is > 0 it will be detected as being a geothermal node.
			_gameMap->setResourceDensity( _energy, int(pos.x/8) , int(pos.z/8), 255.0f);
		}
		else if( def->reclaimable )
		{

			// TODO: Use the UnitBuilder to construct the consumable units
			// This includes things such as:
			// Trees, rocks, animals, unit corpses
		}
	}



	_gameMap->setGravity( getAICallback()->GetGravity() );
	_gameMap->setMaxWindSpeed( getAICallback()->GetMaxWind() );
	_gameMap->setMinWindSpeed( getAICallback()->GetMinWind() );
	_gameMap->setTidalStrength( getAICallback()->GetTidalStrength() );
}

const OOInterface::GameMap& SpringAIInterface::getGameMap()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::getGameMap> " << std::endl;
	//TODO: Add the ability to refresh certain aspects of the GameMap
	// Features: Trees, etc need to be checked if they are still there
	// Change in gravity?
	// Change in elevations of the terrain (don't check this too often )

	return *_gameMap;
}

void SpringAIInterface::createUnitPrototypes()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::createUnitPrototypes> " << std::endl;
	const UnitDef** list = new const UnitDef*[getAICallback()->GetNumUnitDefs()];

	getAICallback()->GetUnitDefList( list );

	for( int i = 0; i < getAICallback()->GetNumUnitDefs(); ++i )
	{
		//Pre-allocate all of the units so they can refer to each other
		OOInterface::UnitPtr unit( new OOInterface::Unit() );
		unit->setID( list[i]->id );
		unit->setName( list[i]->name );

		_unitPrototypes.insert( unit );
	}


	UnitDirector dir( this );
	OOInterface::CUnitBuilder builder;

	for( OOInterface::UnitSet::iterator it = _unitPrototypes.begin(); it != _unitPrototypes.end(); ++it )
	{
		builder.reset( const_cast< OOInterface::Unit* >( (*it).get() ) );

		for( int i =0; i < getAICallback()->GetNumUnitDefs(); ++i )
			if( list[i]->id == (*it)->getID() )
				dir.build( list[i], builder, _unitPrototypes );
	}


	/**
	for( OOInterface::UnitSet::iterator it = _unitPrototypes.begin(); it != _unitPrototypes.end(); ++it)
	{
		OOInterface::Logger::getInstance("AI.txt") << (*it)->toString() << std::endl;
	}
	**/

	delete[] list;
}

OOInterface::UnitPtr SpringAIInterface::getUnitByID( int id )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::getUnitByID> " << id << std::endl;
	if( id == 0 )
	{
		//Whenever an ID of 0 appears in Spring it indicates the NullUnit basically :(
		//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::getUnitByID> A NULL unit was created. This may be because of bad things." << std::endl;
		OOInterface::UnitPtr nullUnit( new OOInterface::Unit );
		return nullUnit;
	}

	for( OOInterface::UnitSet::const_iterator iter = _units.begin(); iter != _units.end(); ++iter )
	{
		if( (*iter)->getID() == id )
			return (*iter);
	}

	return createUnit( id );
}

OOInterface::UnitPtr SpringAIInterface::createUnit( int id )
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::createUnit> " << id << std::endl;
	if( id == 0 ) {
		//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::createUnit> A NULL unit was created. This may be because of bad things." << std::endl;
		OOInterface::UnitPtr nullUnit( new OOInterface::Unit );
		return nullUnit;
	}

	const UnitDef* def = getAICallback()->GetUnitDef( id );
	if( !def )
	{

	}

	for( OOInterface::UnitSet::const_iterator iter = _unitPrototypes.begin(); iter != _unitPrototypes.end(); ++iter )
	{
		if( (*iter)->getID() == def->id )
		{

			OOInterface::UnitPtr unit( new OOInterface::Unit( *(*iter) ) );
			unit->setID( id );
			unit->setHealth( getAICallback()->GetUnitHealth( id ));

			float3 pos = getAICallback()->GetUnitPos( id );
			unit->setX( int(pos.x / 8.0f) );
			unit->setZ( int(pos.z / 8.0f) );
			unit->setY( _gameMap->getElevation( unit->getX(), unit->getZ() ));

			for( OOInterface::ConstPlayerSet::iterator player = _players.begin(); player != _players.end(); ++player )
				if( (*player)->getID() == getAICallback()->GetUnitTeam( id ) )
				{
					OOInterface::Player* p = const_cast< OOInterface::Player* > ( (*player).get() );
					p->addUnit( unit );
					unit->setOwner( *player );
				}


			_units.insert( unit );
			return unit;
		}
	}

	//If it isn't in the unitPrototype set then it doesnt exist
	assert( false );
}

const OOInterface::ResourceList& SpringAIInterface::getResourceTypes()
{
	if(_resources.size() == 0) {
		_resources.push_back( _metal );
		_resources.push_back( _energy );
	}

	return _resources;
}

OOInterface::CommandFactory& SpringAIInterface::getCommandFactory()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::getCommandFactory> " << std::endl;
	static SpringCommandFactory factory;
	return factory;
}

void SpringAIInterface::flushBuffers()
{
	flushCommandBuffer();
	flushTextBuffer();
}

void SpringAIInterface::flushCommandBuffer()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::flushCommandBuffer> " << std::endl;
	boost::mutex::scoped_lock lock( _cmdBufferMutex );

	while( !_commandBuffer.empty() )
	{
		CommandUnitPair& pair = _commandBuffer.front();


		OOInterface::ConstUnitPtr unit = pair.first;
		OOInterface::ConstCommandPtr command = pair.second;

		boost::shared_ptr< const SpringCommand > cmd = boost::dynamic_pointer_cast< const SpringCommand > ( command );

		if( unit.get() && cmd.get() )
		{
			getAICallback()->GiveOrder( unit->getID(), cmd->getCommand());
		}
		else
			OOInterface::Logger::getInstance("AI.txt") << "Error converting the command to a SpringCommand!!!" << std::endl;

		_commandBuffer.pop();
	}

}

void SpringAIInterface::flushTextBuffer()
{
	//OOInterface::Logger::getInstance("AI.txt") << "SpringAIInterface::flushTextBuffer> " << std::endl;
	boost::mutex::scoped_lock lock( _textBufferMutex );

	while( !_textBuffer.empty() )
	{
		getAICallback()->SendTextMsg( _textBuffer.front().c_str(), 0);
		_textBuffer.pop();
	}
}

